package kz.kstu.scantest.business.server.impl;

import kz.kstu.scantest.business.common.constants.FormDataErrorMessages;
import kz.kstu.scantest.business.common.form.FormType;
import kz.kstu.scantest.business.common.form.builder.AbstractFormBuilder;
import kz.kstu.scantest.business.common.helper.FormDataHelper;
import kz.kstu.scantest.business.entity.CommonEntityConstant;
import kz.kstu.scantest.business.entity.data.*;
import kz.kstu.scantest.business.entity.data.FieldNameEnum;
import kz.kstu.scantest.business.entity.data.SubjectEnum;
import kz.kstu.scantest.business.entity.form.Form;
import kz.kstu.scantest.business.entity.data.FormDataError;
import kz.kstu.scantest.business.entity.guide.School;
import kz.kstu.scantest.business.entity.guide.Statistic;
import kz.kstu.scantest.business.entity.test.Question;
import kz.kstu.scantest.business.entity.test.SubjectQuestion;
import kz.kstu.scantest.business.entity.test.Test;
import kz.kstu.scantest.business.server.api.FormDataService;
import kz.kstu.scantest.business.server.util.FormDataParser;
import kz.kstu.scantest.business.util.StringUtil;
import org.apache.log4j.Logger;

import javax.persistence.NoResultException;
import javax.persistence.NonUniqueResultException;
import javax.persistence.Query;
import java.io.UnsupportedEncodingException;
import java.util.*;

/**
 * @author Filipp_Stankevich
 *         created: 14.09.11 22:23
 */
public class FormDataServiceImpl extends AbstractService implements FormDataService {

    private final static Logger LOGGER = Logger.getLogger(FormDataServiceImpl.class);
    private static final long STATISTIC_ID = 1L;
    private static final int VARIANT_LENGTH = 4;

    @Override
    public List<FormData> checkFormData(List<FormData> formDataList) {
        Statistic statistic = getStatistic();
        for (FormData formData : formDataList) {
            Set<FormDataError> errors = formData.getFormDataErrors();
            if (errors != null) {
                errors.clear();
            }
            Query query = getEntityManager().createNamedQuery(CommonEntityConstant.FIND_TEST_BY_VARIANT);
            query.setParameter("variant", formData.getVariant());
            Test test;
            try {
                test = (Test) query.getSingleResult();
            } catch (NonUniqueResultException e) {
                formData.addFormDataError(FormDataHelper.createFormDataError(FormDataErrorMessages.DUPLICATE_VARIANT));
                LOGGER.error("Duplicate variant = " + formData.getVariant());
                continue;
            } catch (NoResultException e) {
                formData.addFormDataError(FormDataHelper.createFormDataError(FormDataErrorMessages.VARIANT_NOT_FOUND));
                LOGGER.error("Test not found, variant = " + formData.getVariant());
                continue;
            }

            for (SubjectData subjectData : formData.getSubjects()) {
                SubjectEnum subject = subjectData.getSubjectEnum();

                if (subject == null) {
                    formData.addFormDataError(FormDataHelper.createFormDataError(FormDataErrorMessages.SUBJECT_NOT_FOUND));
                    continue;
                }

                List<AnswerData> answers = subjectData.getAnswers();
                SubjectQuestion subjectQuestion = getSubjectQuestionBySubjectEnum(test, subject);
                List<Question> questions = subjectQuestion.getQuestions();
                for (int i = 0; i < questions.size(); i++) {
                    Question question = questions.get(i);
                    AnswerData answerData = answers.get(i);
                    String answer = answerData.getValue();
                    answerData.setRight(answer.equalsIgnoreCase(question.getRightAnswer()));
                }
            }
            if (formData.getProcessDate() == null) {
                statistic.increment();
            }
            formData.setProcessDate(new Date());
        }
        getEntityManager().merge(statistic);
        return formDataList;
    }

    private SubjectQuestion getSubjectQuestionBySubjectEnum(Test test, SubjectEnum subjectEnum) {
        List<SubjectQuestion> questions = test.getSubjects();
        for (SubjectQuestion question : questions) {
            if (subjectEnum == question.getSubject()) {
                return question;
            }
        }
        return null;
    }

    @Override
    public FormData parse(byte[] data, FormType formType) throws UnsupportedEncodingException {
        Form form = AbstractFormBuilder.createForm(formType);
        FormDataParser formDataParser = new FormDataParser(form, data);
        FormData formData = formDataParser.process();
        formData.setCreateDate(new Date());
        return formData;
    }

    private void removeFormDataErrorsById(Long formDataId) {
        Query query = getEntityManager().createNamedQuery(CommonEntityConstant.DELETE_FROM_DATA_ERRORS_BY_ID);
        query.setParameter("id", formDataId);
        query.executeUpdate();
    }

    public FormData validateAndSaveFormData(FormData formData) {
        removeFormDataErrorsById(formData.getId());
        Set<FormDataError> errors = formData.getFormDataErrors();
        if (errors != null) {
            errors.clear();
        }
        String variant = formData.getVariant();
        if (StringUtil.isEmptyString(variant)) {
            FormDataError error = FormDataHelper.createFormDataError(FormDataErrorMessages.EMPTY_VARIANT);
            formData.addFormDataError(error);
        } else {
            if (variant.length() < VARIANT_LENGTH || !StringUtil.isNumber(variant)) {
                FormDataError error = FormDataHelper.createFormDataError(FormDataErrorMessages.ERROR_VARIANT_FORMAT);
                formData.addFormDataError(error);
            }
        }

        SubjectData fifthSubjectData = FormDataHelper.getSubjectDataByFieldNameEnum(formData, FieldNameEnum.FIFTH_SUBJECT_NAME);
        if (fifthSubjectData != null) {
            SubjectEnum fifthSubject = fifthSubjectData.getSubjectEnum();
            if (fifthSubject == null) {
                FormDataError error = FormDataHelper.createFormDataError(FormDataErrorMessages.EMPTY_FIFTH_SUBJECT);
                formData.addFormDataError(error);
            }
        }

        boolean hasErrorAnswer = false;
        for (SubjectData subjectData : formData.getSubjects()) {
            for (AnswerData answerData : subjectData.getAnswers()) {
                if (!FormDataHelper.AVAILABLE_ANSWERS.contains(answerData.getValue())) {
                    hasErrorAnswer = true;
                }
            }
        }
        if (hasErrorAnswer) {
            FormDataError error = FormDataHelper.createFormDataError(FormDataErrorMessages.HAS_INCORRECT_ANSWER);
            formData.addFormDataError(error);
        }

        saveFormData(formData);

        if (hasErrorAnswer) {
            LOGGER.error("FormData has incorrect answer, id = " + formData.getId());
        }

        return formData;
    }

    @Override
    public List<FormData> findByDateAndSchool(Date date, School school) {
        Query query;
        if (school != null) {
            query = getEntityManager().createNamedQuery(CommonEntityConstant.FIND_FORM_DATA_BY_CREATE_DATE_AND_SCHOOL);
            query.setParameter("createDate", date);
            query.setParameter("school", school);
        } else {
            query = getEntityManager().createNamedQuery(CommonEntityConstant.FIND_FORM_DATA_WITHOUT_SCHOOL_BY_CREATE_DATE);
            query.setParameter("createDate", date);
        }
        return query.getResultList();
    }

    @Override
    public List<FormData> findByDateAndSchoolWithErrors(Date date, School school) {
        Query query;
        if (school != null) {
            query = getEntityManager().createNamedQuery(CommonEntityConstant.FIND_FORM_DATA_WITH_ERRORS_BY_DATE_AND_SCHOOL);
            query.setParameter("createDate", date);
            query.setParameter("school", school);
        } else {
            query = getEntityManager().createNamedQuery(CommonEntityConstant.FIND_FORM_DATA_WITH_ERRORS_BY_DATE_WITHOUT_SCHOOL);
            query.setParameter("createDate", date);
        }
        return query.getResultList();
    }

    @Override
    public List<Date> findScanDates() {
        Query query = getEntityManager().createNamedQuery(CommonEntityConstant.FIND_SCAN_DATES);
        return query.getResultList();
    }

    @Override
    public Integer getProcessedFormDataCount() {
        Integer count = getStatistic().getCount();
        return count != null ? count : 0;
    }

    @Override
    public void changeStatistic(Integer count) {
        Statistic statistic = getStatistic();
        statistic.setCount(count);
        save(statistic);
    }

    public void saveFormData(FormData formData) {
        save(formData);
    }

    public void removeFormData(Long id) {
        remove(FormData.class, id);
    }

    public FormData findFormDataById(Long id) {
        return getEntityManager().find(FormData.class, id);
    }

    private Statistic getStatistic() {
        Statistic statistic = getEntityManager().find(Statistic.class, STATISTIC_ID);
        if (statistic == null) {
            statistic = new Statistic();
            statistic.setId(STATISTIC_ID);
        }
        return statistic;
    }
}
